﻿using UnityEngine;
using System.Collections;
using System.Collections.Generic;

namespace ETModel {

    /// <summary>
    /// 条形拖轨特效<para/>
    /// 使用说明:<para/>
    /// 1. 效果组件的增加:找到需要有效果的节点,对该节点增加一个子节点(称为"效果节点"),添加本组件,效果节点的up为条形方向,设置材质以及长度等参数<para/>
    /// 2.1. 自己调用渲染效果的方法:Update里调用Itterate(Time.time)+UpdateTrail(Time.time, Time.deltaTime)
    /// 2.2. 动画播放时渲染效果的方法:animator的状态节点增加BarTrailEffectRander脚本,并配置对应的效果节点名<para/>
    /// 2.3. 动画中制定时间段渲染的方法: animator的状态节点BarTrailEffectRander的属性OnStateEnterRanding设置为false,给模型节点(准确的定义是Animator组件所在的节点)增加BarTrailEffectController脚本
    /// ,动画事件中调用 BarTrailEffectController.GameObjectTrailStart(效果节点名) 和 BarTrailEffectController.GameObjectTrailStop(效果节点名)<para/>
    /// </summary>
    [AddComponentMenu("Effects/BarTrailEffect")]
    public class BarTrailEffect : MonoBehaviour {

        /*
         Generates a trail that is always facing upwards using the scriptable mesh interface. (This is from the Tron Trails in Unity)
         vertex colors and uv's are generated similar to the builtin Trail Renderer. But it DOES NOT RUN ITSELF LIKE THE TRAIL RENDERER. (there is no update method)
         To use it
         1. Create an empty game object (your weapon), with it's y axis pointing along the weapons blade.
         2. Attach this script and a MeshRenderer
         3. Then assign a particle material to the mesh renderer
         4. Call it's Itterate method everytime you sample the animation (if you want a smooth trail this should be more than once a frame)
         5. Call it's UpdateTrail method once a frame to rebuild the mesh
        */
        #region Public
        public Material trailMaterial;
        /// <summary>
        /// 条形长度
        /// </summary>
        public float height = 2.0f;
        /// <summary>
        /// 
        /// </summary>
        public float trailTime = 2.0f;
        public bool alwaysUp = false;
        public float minDistance = 0.1f;
        public float timeTransitionSpeed = 1f;
        public float desiredTime = 2.0f;
        public Color startColor = Color.white;
        public Color endColor = new Color(1, 1, 1, 0);
        /// <summary>
        /// 初始配置的参数
        /// </summary>
        public BarTrailEffectParams DefaultParams;
        #endregion
        //
        #region Temporary
        Vector3 position;
        float now = 0;
        TronTrailSection currentSection;
        Matrix4x4 localSpaceTransform;
        #endregion

        #region Internal
        private GameObject meshObj;
        private MeshRenderer meshRender;
        private Mesh mesh;
        private Vector3[] vertices;
        private Color[] colors;
        private Vector2[] uv;
        #endregion

        private List<TronTrailSection> sections = new List<TronTrailSection>();

        void Awake() {

            mesh = new Mesh();
            var meshObj = new GameObject();
            meshObj.name = name + "_build";
            var mFilter = meshObj.AddComponent<MeshFilter>();
            mFilter.mesh = mesh;
            meshRender = meshObj.AddComponent<MeshRenderer>();
            meshRender.sharedMaterial = trailMaterial;

            DefaultParams = new BarTrailEffectParams {
                trailTime = trailTime,
                desiredTime = desiredTime,
                timeTransitionSpeed = timeTransitionSpeed,
            };
        }
        private void OnDestroy() {
            Destroy(mesh);
            Destroy(meshObj);
        }
        public void StartTrail(float _desiredTime, float fadeInTime) {
            desiredTime = _desiredTime;
            if (trailTime != desiredTime) {
                timeTransitionSpeed = Mathf.Abs(desiredTime - trailTime) / fadeInTime;
            }
            if (trailTime <= 0) {
                trailTime = 0.01f;
            }
        }
        public void SetTime(float _trailTime, float _desiredTime, float _timeTransitionSpeed) {
            trailTime = _trailTime;
            desiredTime = _desiredTime;
            timeTransitionSpeed = _timeTransitionSpeed;
            if (trailTime <= 0) {
                ClearTrail();
            }
        }
        public void FadeOut(float fadeTime) {
            desiredTime = 0;
            if (trailTime > 0) {
                timeTransitionSpeed = trailTime / fadeTime;
            }
        }
        public void SetTrailColor(Color color) {
            trailMaterial.SetColor("_TintColor", color);
        }
        public void Itterate(float itterateTime) { // ** call everytime you sample animation **

            position = transform.position;
            now = itterateTime;

            // Add a new trail section
            if (sections.Count == 0 || (sections[0].point - position).sqrMagnitude > minDistance * minDistance) {
                TronTrailSection section = new TronTrailSection();
                section.point = position;
                if (alwaysUp)
                    section.upDir = Vector3.up;
                else
                    section.upDir = transform.TransformDirection(Vector3.up);

                section.time = now;
                sections.Insert(0, section);
            }
        }
        public void UpdateTrail(float currentTime, float deltaTime) { // ** call once a frame **

            // Rebuild the mesh	
            mesh.Clear();
            //
            // Remove old sections
            while (sections.Count > 0 && currentTime > sections[sections.Count - 1].time + trailTime) {
                sections.RemoveAt(sections.Count - 1);
            }
            // We need at least 2 sections to create the line
            if (sections.Count < 2)
                return;
            //
            vertices = new Vector3[sections.Count * 2];
            colors = new Color[sections.Count * 2];
            uv = new Vector2[sections.Count * 2];
            //
            currentSection = sections[0];
            //
            // Use matrix instead of transform.TransformPoint for performance reasons
            //localSpaceTransform = transform.worldToLocalMatrix;

            // Generate vertex, uv and colors
            for (var i = 0; i < sections.Count; i++) {
                //
                currentSection = sections[i];
                // Calculate u for texture uv and color interpolation
                float u = 0.0f;
                if (i != 0)
                    u = Mathf.Clamp01((currentTime - currentSection.time) / trailTime);
                //
                // Calculate upwards direction
                Vector3 upDir = currentSection.upDir;

                // Generate vertices
                vertices[i * 2 + 0] = currentSection.point;
                vertices[i * 2 + 1] = currentSection.point + upDir * height;

                uv[i * 2 + 0] = new Vector2(u, 0);
                uv[i * 2 + 1] = new Vector2(u, 1);

                // fade colors out over time
                Color interpolatedColor = Color.Lerp(startColor, endColor, u);
                colors[i * 2 + 0] = interpolatedColor;
                colors[i * 2 + 1] = interpolatedColor;
            }

            // Generate triangles indices
            int[] triangles = new int[(sections.Count - 1) * 2 * 3];
            for (int i = 0; i < triangles.Length / 6; i++) {
                triangles[i * 6 + 0] = i * 2;
                triangles[i * 6 + 1] = i * 2 + 1;
                triangles[i * 6 + 2] = i * 2 + 2;

                triangles[i * 6 + 3] = i * 2 + 2;
                triangles[i * 6 + 4] = i * 2 + 1;
                triangles[i * 6 + 5] = i * 2 + 3;
            }

            // Assign to mesh	
            mesh.vertices = vertices;
            mesh.colors = colors;
            mesh.uv = uv;
            mesh.triangles = triangles;
            //mesh.RecalculateNormals();
            //
            // Tween to the desired time
            //
            if (trailTime > desiredTime) {
                trailTime -= deltaTime * timeTransitionSpeed;
                if (trailTime <= desiredTime) trailTime = desiredTime;
            } else if (trailTime < desiredTime) {
                trailTime += deltaTime * timeTransitionSpeed;
                if (trailTime >= desiredTime) trailTime = desiredTime;
            }
        }
        public void ClearTrail() {
            desiredTime = 0;
            trailTime = 0;
            if (mesh != null) {
                mesh.Clear();
                sections.Clear();
            }
        }




        /*
        private float t = 0.033f;
        //private float tempT = 0;
        //private float animationIncrement = 0.003f;
        void Update() {
            if (trailTime <= 0) {
                return;
            }

            t = Mathf.Clamp(Time.deltaTime, 0, 0.066f);

            Itterate(Time.time);
            UpdateTrail(Time.time, t);
        }*/
    }



    public class TronTrailSection {
        public Vector3 point;
        public Vector3 upDir;
        public float time;
        public TronTrailSection() {

        }
        public TronTrailSection(Vector3 p, float t) {
            point = p;
            time = t;
        }
    }
    public class BarTrailEffectParams {
        public float trailTime = 2.0f;
        public float timeTransitionSpeed = 1f;
        public float desiredTime = 2.0f;
    }

}
